use std::{fs::File, io, process::Command};

use clap::Parser;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct Porjects {
    git_name: String,
    git_email: String,
    repos: Vec<Repository>,
}

#[derive(Debug, Deserialize)]
struct Repository {
    name: String,
    url: String,
}

#[derive(Parser, Debug)]
struct Args {
    /// projects file
    file: String,
}

fn main() {
    let args = Args::parse();
    let f =
        File::open(args.file.clone()).unwrap_or_else(|_| panic!("open file {} fail", args.file));
    let projects: Porjects = serde_yaml::from_reader(f).expect("msg");
    println!(
        "start to git clone projects, count: {}",
        projects.repos.len()
    );
    let git_name = projects.git_name;
    let git_email = projects.git_email;
    for repo in projects.repos {
        if let Err(e) = git_clone(&git_name, &git_email, &repo.name, &repo.url) {
            println!("git clone {} fail: {}", repo.url, e);
        }
    }
    println!("pull code finished");
}

fn git_clone(git_name: &str, git_email: &str, name: &str, url: &str) -> io::Result<()> {
    let mut git = Command::new("git");
    git.arg("clone");
    if !git_name.is_empty() {
        git.args(["-c", &format!("user.name={}", git_name)]);
    }
    if !git_email.is_empty() {
        git.args(["-c", &format!("user.email={}", git_email)]);
    }
    git.arg(url);
    if name.ends_with('/') {
        let repo_name =
            get_repo_name(url).ok_or(io::Error::new(io::ErrorKind::Other, "get repo name fail"))?;
        git.arg(name.to_string() + repo_name);
    } else if !name.is_empty() {
        git.arg(name);
    }
    git.stdout(io::stdout()).status()?;
    println!("git clone {} finished", url);
    Ok(())
}

fn get_repo_name(url: &str) -> Option<&str> {
    let end = url.rfind('.')?;
    let start = url.rfind('/')?;
    url.get(start + 1..end)
}

#[test]
fn test_get_repo_name() {
    let repo = "https://gitee.com/yscsky/some_project.git";
    let name = get_repo_name(repo).unwrap();
    println!("{name}");
}
